home *** CD-ROM | disk | FTP | other *** search
- /********************************************************************************
-
- Gnucleus - A node application for the Gnutella network
- Copyright (C) 2000 John Marshall
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- For support, questions, comments, etc...
- E-Mail:
- swabby@c0re.net
-
- Address:
- 21 Cadogan Way
- Nashua, NH, USA 03062
-
- ********************************************************************************/
-
- #include "stdafx.h"
- #include <assert.h>
- #include "ListCtrlEx.h"
-
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #undef THIS_FILE
- static char THIS_FILE[] = __FILE__;
- #endif
-
- // Used for inplace edits on the control
- #include "InPlaceEdit.h"
-
- /////////////////////////////////////////////////////////////////////////////
- // CListCtrlEx
-
- IMPLEMENT_DYNCREATE(CListCtrlEx, CListCtrl)
-
- BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
- //{{AFX_MSG_MAP(CListCtrlEx)
- ON_WM_PAINT()
- ON_WM_SETFOCUS()
- ON_WM_KILLFOCUS()
- ON_WM_MOUSEMOVE()
- ON_WM_LBUTTONDOWN()
- ON_WM_KEYDOWN()
- ON_WM_DESTROY()
- ON_NOTIFY_REFLECT(LVN_ENDLABELEDIT, OnEndlabeledit)
- //}}AFX_MSG_MAP
- ON_NOTIFY(HDN_ITEMCLICKA, 0, OnHeaderClicked)
- ON_NOTIFY(HDN_ITEMCLICKW, 0, OnHeaderClicked)
- ON_MESSAGE(LVM_SETTEXTCOLOR, OnSetTextColor)
- ON_MESSAGE(LVM_SETTEXTBKCOLOR, OnSetTextBkColor)
- ON_MESSAGE(LVM_SETBKCOLOR, OnSetBkColor)
- END_MESSAGE_MAP()
-
- /////////////////////////////////////////////////////////////////////////////
- // CListCtrlEx construction/destruction
-
- CListCtrlEx::CListCtrlEx()
- {
- // Don't Highlight entire row by default.
- m_bFullRowSel = FALSE;
- // Allow selections to be made all the way across the rows
- m_bClientWidthSel = TRUE;
-
- // Default to not showing the grid lines.
- m_bDrawGrid = FALSE;
-
- // Set default sorting behaviour
- nSortedCol = -1;
- bSortAscending = TRUE;
-
- // Allow the columns to be re-arranged
- m_headerctrl.SetCallback( this, (void (CWnd::*)(int, int))DragColumn );
-
- m_clrText = ::GetSysColor(COLOR_WINDOWTEXT);
- m_clrTextBk = ::GetSysColor(COLOR_WINDOW);
- m_clrBkgnd = ::GetSysColor(COLOR_WINDOW);
-
- // Use generic sort routine by default, disable and enable
- // with EnableGenericSort().
- m_bUseGenericSort = TRUE;
-
-
- }
-
- CListCtrlEx::~CListCtrlEx()
- {
- }
-
- /**
- * Find the number of columns in the control
- */
- int CListCtrlEx::GetNColumns()
- {
- return( m_headerctrl.GetItemCount() );
- }
-
- CString CListCtrlEx::GetColumnTitle( int n )
- {
- // Get the column text and format
- TCHAR buf[256];
- HD_ITEM hditem;
-
- hditem.mask = HDI_TEXT | HDI_FORMAT;
- hditem.pszText = buf;
- hditem.cchTextMax = 255;
-
- m_headerctrl.GetItem( n , &hditem );
- return( CString ( buf ) );
- }
-
- /**
- * Return the column number associated with a title.
- * This is necessary because the normal Windows method for adding
- * text to a row depends upon knowing the column number for which
- * you wish to insert text. But we hijack the column numbers by
- * allowing users to rearrange them...
- */
- int CListCtrlEx::GetColumnNumber( char *title )
- {
- int nColumns = m_headerctrl.GetItemCount();
-
- for ( int i = 0; i < nColumns; i++ )
- {
- HD_ITEM data;
- char name[200];
-
- data.mask = HDI_TEXT;
- data.pszText = (LPSTR)name;
- data.cchTextMax = sizeof( name );
-
- m_headerctrl.GetItem( i, &data );
-
-
- if ( strcmp( title, data.pszText) == 0 )
- {
- return( i );
- }
- }
-
- return( -1 );
- }
-
- /**
- * Used for the header column dragging behaviour
- */
- void CListCtrlEx::DragColumn(int source, int dest)
- {
- TCHAR sColText[160];
-
- // Insert a column at dest
- LV_COLUMN lv_col;
- lv_col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
- lv_col.pszText = sColText;
- lv_col.cchTextMax = 159;
- GetColumn( source, &lv_col );
- lv_col.iSubItem = dest;
- InsertColumn( dest, &lv_col );
-
- // Adjust source col number since it might have changed
- // because a new column was inserted
- if( source > dest )
- source++;
-
- // Moving a col to position 0 is a special case
- if( dest == 0 )
- for( int i = GetItemCount()-1; i > -1 ; i-- )
- SetItemText(i, 1, GetItemText( i, 0) );
-
-
- // Copy sub item from source to dest
- for( int i = GetItemCount()-1; i > -1 ; i-- )
- SetItemText(i, dest, GetItemText( i, source ) );
-
- // Delete the source column, but not if it is the first
- if( source != 0 )
- DeleteColumn( source );
- else
- {
- // If source col is 0, then copy col# 1 to col#0
- // and then delete col# 1
- GetColumn( 1, &lv_col );
- lv_col.iSubItem = 0;
- SetColumn( 0, &lv_col );
- for( int i = GetItemCount()-1; i > -1 ; i-- )
- SetItemText(i, 0, GetItemText( i, 1) );
- DeleteColumn( 1 );
- }
-
- Invalidate();
- }
-
- /**
- * Make sure the control is owner drawn, this means that we can
- * provide the custom drawing behaviour of; full row selection, and
- * grid lines
- */
- BOOL CListCtrlEx::PreCreateWindow(CREATESTRUCT& cs)
- {
- // default is report view and full row selection
- cs.style &= ~LVS_TYPEMASK;
- cs.style |= LVS_REPORT | LVS_OWNERDRAWFIXED | LVS_EDITLABELS ;
- m_bFullRowSel = TRUE;
-
- return(CListCtrl::PreCreateWindow(cs));
- }
-
-
- /**
- * Hook into the message loop to show the tooltips.
- */
- BOOL CListCtrlEx::PreTranslateMessage(MSG* pMsg)
- {
- m_tooltip.RelayEvent( pMsg );
- return CListCtrl::PreTranslateMessage(pMsg);
- }
-
-
- /**
- * Allow the whole row to be highlighted in the list, not just the first
- * column.
- */
- BOOL CListCtrlEx::SetFullRowSel(BOOL bFullRowSel)
- {
- // no painting during change
- LockWindowUpdate();
-
- m_bFullRowSel = bFullRowSel;
-
- BOOL bRet;
-
- if (m_bFullRowSel)
- bRet = ModifyStyle(0L, LVS_OWNERDRAWFIXED);
- else
- bRet = ModifyStyle(LVS_OWNERDRAWFIXED, 0L);
-
- // repaint window if we are not changing view type
- if (bRet && (GetStyle() & LVS_TYPEMASK) == LVS_REPORT)
- Invalidate();
-
- // repaint changes
- UnlockWindowUpdate();
-
- return(bRet);
- }
-
- /**
- * Determine whether or not full row selection is enabled.
- */
- BOOL CListCtrlEx::GetFullRowSel()
- {
- return(m_bFullRowSel);
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // CListCtrlEx drawing
-
- /*
- * DrawItem() is called by the framework whenever an item needs to be drawn
- * for owner drawn controls.
- * Note:
- * <UL>
- * <LI>LVS_SHOWSELALWAYS: non owner drawn controls show an item is
- * highlighted when the control does not have focus with a different
- * highlight color is (usually gray). This is not supported for
- * this control.
- * </UL>
- */
-
- void CListCtrlEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
- {
- CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
- int iSavedDC = pDC->SaveDC(); // Save DC state
-
- int iItem = lpDrawItemStruct->itemID;
-
- // Get item image and state info
- LV_ITEM lvi;
- lvi.mask = LVIF_IMAGE | LVIF_STATE;
- lvi.iItem = iItem;
- lvi.iSubItem = 0;
- lvi.stateMask = 0xFFFF; // get all state flags
- GetItem(&lvi);
-
- bool bHighlight = (
- (lvi.state & LVIS_DROPHILITED) ||
- ((lvi.state & LVIS_SELECTED) && ((GetFocus() == this) || (GetStyle() & LVS_SHOWSELALWAYS)))
- );
-
- // Get rectangles for drawing
- CRect rcBounds;
- CRect rcLabel;
- CRect rcIcon;
- GetItemRect(iItem, rcBounds, LVIR_BOUNDS);
- GetItemRect(iItem, rcLabel, LVIR_LABEL);
- GetItemRect(iItem, rcIcon, LVIR_ICON);
- CRect rcItem(rcBounds);
-
- CString sLabel = GetItemText(iItem, 0);
-
- // Labels are offset by a certain amount
- // This offset is related to the width of a space character
- int offset = pDC->GetTextExtent(_T(" "), 1 ).cx*2;
-
- rcBounds.left = rcLabel.left;
- CRect rcWnd;
- GetClientRect(&rcWnd);
- if(m_bClientWidthSel && rcBounds.right<rcWnd.right)
- rcBounds.right = rcWnd.right;
-
- // Draw the background
- if(bHighlight)
- {
- pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
- pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));
- pDC->FillRect(rcBounds, &CBrush(::GetSysColor(COLOR_HIGHLIGHT)));
- }
- else
- {
- pDC->FillRect(rcBounds, &CBrush(m_clrTextBk));
- }
-
- // Set clip region
- rcItem.right = rcItem.left + GetColumnWidth(0);
-
- // Draw state icon
- if(lvi.state & LVIS_STATEIMAGEMASK)
- {
- int nImage = ((lvi.state & LVIS_STATEIMAGEMASK)>>12) - 1;
- CImageList* pImageList = GetImageList(LVSIL_STATE);
- if(pImageList)
- {
- pImageList->Draw(pDC, nImage,
- CPoint(rcItem.left, rcItem.top), ILD_TRANSPARENT);
- }
- }
-
- // Draw normal and overlay icon
- CImageList* pImageList = GetImageList(LVSIL_SMALL);
- if(pImageList)
- {
- UINT nOvlImageMask = lvi.state & LVIS_OVERLAYMASK;
- pImageList->Draw(pDC, lvi.iImage,
- CPoint(rcIcon.left, rcIcon.top),
- (bHighlight?ILD_BLEND50:0) | ILD_TRANSPARENT | nOvlImageMask );
- }
-
- // Draw item label - Column 0
- rcLabel.left += offset/2-1;
- rcLabel.right -= offset;
- pDC->DrawText(sLabel,-1,rcLabel,DT_LEFT | DT_SINGLELINE | DT_NOPREFIX
- | DT_VCENTER | DT_END_ELLIPSIS);
-
- // Draw labels for remaining columns
- LV_COLUMN lvc;
- lvc.mask = LVCF_FMT | LVCF_WIDTH;
-
- for(int nColumn = 1; GetColumn(nColumn, &lvc); nColumn++)
- {
- rcItem.left = rcItem.right;
- rcItem.right += lvc.cx;
-
- sLabel = GetItemText(iItem, nColumn);
-
- // Get the text justification
- UINT nJustify = DT_LEFT;
- switch(lvc.fmt & LVCFMT_JUSTIFYMASK)
- {
- case LVCFMT_RIGHT:
- nJustify = DT_RIGHT;
- break;
- case LVCFMT_CENTER:
- nJustify = DT_CENTER;
- break;
- default:
- break;
- }
-
- rcLabel = rcItem;
- rcLabel.left += offset;
- rcLabel.right -= offset;
-
- pDC->DrawText(sLabel, -1, rcLabel,
- nJustify | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER | DT_END_ELLIPSIS);
- }
-
- // draw focus rectangle if item has focus
- if ((lvi.state & LVIS_FOCUSED) && (GetFocus() == this))
- pDC->DrawFocusRect(rcBounds);
-
-
- pDC->RestoreDC(iSavedDC); // Restore DC.
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // CListCtrlEx diagnostics
-
- #ifdef _DEBUG
-
- void CListCtrlEx::Dump(CDumpContext& dc) const
- {
- CListCtrl::Dump(dc);
-
- dc << "m_bFullRowSel = " << m_bFullRowSel;
- dc << "\n";
- }
-
- #endif //_DEBUG
-
-
- /**
- * @param iRow [in] row of cell
- * @param iColunm [in] column of cell
- * @return Rectangle corresponding to the given cell.
- */
-
- CRect CListCtrlEx::GetCellRect(int iRow, int iColumn)const
- {
- // Make sure that the ListView is in LVS_REPORT
- if((GetStyle() & LVS_TYPEMASK) != LVS_REPORT)
- return CRect(0,0,0,0);
-
- // Get the number of columns
- {
- CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
- int iColumnCount = pHeader->GetItemCount();
- ASSERT(iColumn < iColumnCount);
- }
-
- CRect rect;
- GetItemRect(iRow, &rect, LVIR_BOUNDS);
- // Now find the column
- for(int colnum = 0; colnum < iColumn; colnum++)
- {
- rect.left += GetTrueColumnWidth(colnum);
- }
-
- // Found the column
- rect.right = rect.left + GetTrueColumnWidth(iColumn);
-
- RECT rectClient;
- GetClientRect(&rectClient);
- if(rect.right > rectClient.right)
- rect.right = rectClient.right;
-
- return rect;
- }
-
- /**
- * @author Mark Findlay
- */
- CString CListCtrlEx::GetTrueItemText(int row, int col)const
- {
- // Get the header control
- CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
- _ASSERTE(pHeader);
-
- // get the current number of columns
- int nCount = pHeader->GetItemCount();
-
- // find the actual column requested. We will compare
- // against hi.iOrder
- for (int x=0; x< nCount; x++)
- {
- HD_ITEM hi = {0};
- hi.mask = HDI_ORDER;
-
- BOOL bRet = pHeader->GetItem(x,&hi);
- _ASSERTE(bRet);
- if (hi.iOrder == col)
- {
- // Found it, get the associated text
- return GetItemText(row,x);
- }
- }
-
- _ASSERTE(FALSE);
- return "We better never fall through to here!";
- }
-
- /**
- * @author Mark Findlay
- */
- int CListCtrlEx::GetTrueColumnWidth(int nCurrentPosition)const
- {
- CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
- _ASSERTE(pHeader);
-
- int nCount = pHeader->GetItemCount();
-
- for (int x=0; x< nCount; x++)
- {
- HD_ITEM hi = {0};
- hi.mask = HDI_WIDTH | HDI_ORDER;
-
- BOOL bRet = pHeader->GetItem(x,&hi);
- _ASSERTE(bRet);
- if (hi.iOrder == nCurrentPosition)
- return hi.cxy;
- }
-
- _ASSERTE(FALSE);
- return 0; // We would never fall through to here!
- }
-
- void CListCtrlEx::HideTitleTip()
- {
- m_titletip.ShowWindow(SW_HIDE);
- }
-
- /**
- * @param point [in] point in client coordinates
- * @param iRow [out] row containing the point
- * @param iColunm [out] column containing the point
- *
- * @author Matthew Bells
- */
- bool CListCtrlEx::HitTestRowCol(CPoint& point, int& iRow, int& iColumn)
- {
- // Make sure that the ListView is in LVS_REPORT
- if((GetStyle() & LVS_TYPEMASK) != LVS_REPORT)
- return false;
-
- int iPosX = point.x;
- if(m_bFullRowSel)
- iRow = HitTest(point);
- // the above doesn't work when the control isn't owner drawn
- // this will work on machines with Version 4.70 and later of Comctl32.dll.
- else
- {
- LVHITTESTINFO hit_test_info;
- hit_test_info.pt = point;
- SubItemHitTest(&hit_test_info);
- iRow = hit_test_info.iItem;
- }
-
- // Get the number of columns
- CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
- int iColumnCount = pHeader->GetItemCount();
-
- for(iColumn = 0; iColumn < iColumnCount; ++iColumn)
- {
- iPosX -= GetTrueColumnWidth(iColumn);
- if(iPosX < 0)
- break;
- }
- if(iColumn == iColumnCount)
- iColumn = -1;
-
- return (iRow != -1 && iColumn != -1);
- }
-
- // HitTestEx - Determine the row index and column index for a point
- // Returns - the row index or -1 if point is not over a row
- // point - point to be tested.
- // col - to hold the column index
- int CListCtrlEx::HitTestEx(CPoint &point, int *col) const
- {
- int colnum = 0;
- int row = HitTest( point, NULL );
-
- if( col ) *col = 0;
-
- // Make sure that the ListView is in LVS_REPORT
- if( (GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT )
- return row;
-
- // Get the top and bottom row visible
- row = GetTopIndex();
- int bottom = row + GetCountPerPage();
- if( bottom > GetItemCount() )
- bottom = GetItemCount();
-
- // Get the number of columns
- CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
- int nColumnCount = pHeader->GetItemCount();
-
- // Loop through the visible rows
- for( ;row <=bottom;row++)
- {
- // Get bounding rect of item and check whether point falls in it.
- CRect rect;
- GetItemRect( row, &rect, LVIR_BOUNDS );
- if( rect.PtInRect(point) )
- {
- // Now find the column
- for( colnum = 0; colnum < nColumnCount; colnum++ )
- {
- int colwidth = GetColumnWidth(colnum);
- if( point.x >= rect.left
- && point.x <= (rect.left + colwidth ) )
- {
- if( col ) *col = colnum;
- return row;
- }
- rect.left += colwidth;
- }
- }
- }
- return -1;
- }
-
-
- // EditSubLabel - Start edit of a sub item label
- // Returns - Temporary pointer to the new edit control
- // nItem - The row index of the item to edit
- // nCol - The column of the sub item.
- CEdit* CListCtrlEx::EditSubLabel( int nItem, int nCol )
- {
- // The returned pointer should not be saved
-
- // Make sure that the item is visible
- if( !EnsureVisible( nItem, TRUE ) ) return NULL;
-
- // Make sure that nCol is valid
- CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
- int nColumnCount = pHeader->GetItemCount();
- if( nCol >= nColumnCount || GetColumnWidth(nCol) < 5 )
- return NULL;
-
- // Get the column offset
- int offset = 0;
- for( int i = 0; i < nCol; i++ )
- offset += GetColumnWidth( i );
-
- CRect rect;
- GetItemRect( nItem, &rect, LVIR_BOUNDS );
-
- // Now scroll if we need to expose the column
- CRect rcClient;
- GetClientRect( &rcClient );
- if( offset + rect.left < 0 || offset + rect.left > rcClient.right )
- {
- CSize size;
- size.cx = offset + rect.left;
- size.cy = 0;
- Scroll( size );
- rect.left -= size.cx;
- }
-
- // Get Column alignment
- LV_COLUMN lvcol;
- lvcol.mask = LVCF_FMT;
- GetColumn( nCol, &lvcol );
- DWORD dwStyle ;
- if((lvcol.fmt&LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
- dwStyle = ES_LEFT;
- else if((lvcol.fmt&LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
- dwStyle = ES_RIGHT;
- else dwStyle = ES_CENTER;
-
- rect.left += offset+4;
- rect.right = rect.left + GetColumnWidth( nCol ) - 3 ;
- if( rect.right > rcClient.right) rect.right = rcClient.right;
-
- dwStyle |= WS_BORDER|WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL;
- CEdit *pEdit = new CInPlaceEdit(nItem, nCol, GetItemText( nItem, nCol ));
-
- #define IDC_IPEDIT 2
- pEdit->Create( dwStyle, rect, this, IDC_IPEDIT );
-
-
- return pEdit;
- }
-
-
- /**
- * Repaint the selected items.
- */
- void CListCtrlEx::RepaintSelectedItems()
- {
- CRect rcItem;
- CRect rcLabel;
-
- // Invalidate focused item so it can repaint
-
- int iItem = GetNextItem(-1, LVNI_FOCUSED);
-
- if(iItem != -1)
- {
- GetItemRect(iItem, rcItem, LVIR_BOUNDS);
- GetItemRect(iItem, rcLabel, LVIR_LABEL);
- rcItem.left = rcLabel.left;
-
- InvalidateRect(rcItem, FALSE);
- }
-
- // Invalidate selected items depending on LVS_SHOWSELALWAYS
-
- if(!(GetStyle() & LVS_SHOWSELALWAYS))
- {
- for(iItem = GetNextItem(-1, LVNI_SELECTED);
- iItem != -1; iItem = GetNextItem(iItem, LVNI_SELECTED))
- {
- GetItemRect(iItem, rcItem, LVIR_BOUNDS);
- GetItemRect(iItem, rcLabel, LVIR_LABEL);
- rcItem.left = rcLabel.left;
-
- InvalidateRect(rcItem, FALSE);
- }
- }
-
- UpdateWindow();
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // CListCtrlEx message handlers
-
- void CListCtrlEx::OnDestroy()
- {
- m_titletip.DestroyWindow();
-
- CListCtrl::OnDestroy();
- }
-
- void CListCtrlEx::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
- {
- CListCtrl::OnChar(nChar, nRepCnt, nFlags);
- HideTitleTip();
- SendSelChangedNotification();
- }
-
- void CListCtrlEx::OnKillFocus(CWnd* pNewWnd)
- {
- CListCtrl::OnKillFocus(pNewWnd);
-
- // This should be hidden no matter if another control is getting focus
- // or the edit box.
- HideTitleTip();
-
- // this really still has focus if one of its chilren (ie. the edit box)
- // has focus
- if(pNewWnd != NULL && pNewWnd->GetParent() == this)
- return;
-
- // repaint items that should change appearance
- if(m_bFullRowSel && (GetStyle() & LVS_TYPEMASK) == LVS_REPORT)
- RepaintSelectedItems();
- }
-
- void CListCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)
- {
- int iTest = GetKeyState(VK_LMENU);
- // Shortcut to editing.
- if((GetKeyState(VK_LMENU) & 0x8000) || (GetKeyState(VK_RMENU) & 0x8000))
- {
- int iRow;
- int iColumn;
- if(HitTestRowCol(point, iRow, iColumn))
- {
- SetFocus();
- PostMessage(LVM_EDITLABEL, (WPARAM)iRow, 0);
- }
- }
- else
- {
- CListCtrl::OnLButtonDown(nFlags, point);
- ShowTitleTip(point); // Make sure TitleTip changes if needed.
- SendSelChangedNotification();
- }
- }
-
- void CListCtrlEx::OnMouseMove(UINT nFlags, CPoint point)
- {
- if( nFlags == 0 )
- {
- ShowTitleTip(point); // Make sure TitleTip changes if needed.
- }
-
- CListCtrl::OnMouseMove(nFlags, point);
- }
-
- /*
- * When the regular list view control repaints an item, it repaints only the
- * area occupied by defined columns. If the last column does not extend to the
- * end of the client area, then the space to the right of the last column is
- * not repainted. If we are highlighting the full row then this area also needs
- * to be invalidated so that the code in DrawItem() can add or remove the
- * highlighting from this area.
- */
-
- void CListCtrlEx::OnPaint()
- {
- // in full row select mode, we need to extend the clipping region
- // so we can paint a selection all the way to the right
- if (m_bClientWidthSel &&
- (GetStyle() & LVS_TYPEMASK) == LVS_REPORT && GetFullRowSel())
- {
- CRect rcAllLabels;
- GetItemRect(0, rcAllLabels, LVIR_BOUNDS);
-
- CRect rcClient;
- GetClientRect(&rcClient);
- if(rcAllLabels.right < rcClient.right)
- {
- // need to call BeginPaint (in CPaintDC c-tor)
- // to get correct clipping rect
- CPaintDC dc(this);
-
- CRect rcClip;
- dc.GetClipBox(rcClip);
-
- rcClip.left = min(rcAllLabels.right-1, rcClip.left);
- rcClip.right = rcClient.right;
-
- InvalidateRect(rcClip, FALSE);
- // EndPaint will be called in CPaintDC d-tor
- }
- }
-
- CListCtrl::OnPaint();
-
- // Draw the lines only for LVS_REPORT mode
- if ( ( (GetStyle() & LVS_TYPEMASK) == LVS_REPORT ) &&
- ( m_bDrawGrid ) )
- {
- // Get the number of columns
- CClientDC dc(this );
- CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
- int nColumnCount = pHeader->GetItemCount();
-
- // The bottom of the header corresponds to the top of the line
- RECT rect;
- pHeader->GetClientRect( &rect );
- int top = rect.bottom;
-
- // Now get the client rect so we know the line length and
- // when to stop
- GetClientRect( &rect );
-
- // The border of the column is offset by the horz scroll
- int borderx = 0 - GetScrollPos( SB_HORZ );
- for( int i = 0; i < nColumnCount; i++ )
- {
- // Get the next border
- borderx += GetColumnWidth( i );
-
- // if next border is outside client area, break out
- if( borderx >= rect.right ) break;
-
- // Draw the line.
- dc.MoveTo( borderx-1, top);
- dc.LineTo( borderx-1, rect.bottom );
- }
-
- // Draw the horizontal grid lines
-
- // First get the height
- if( !GetItemRect( 0, &rect, LVIR_BOUNDS ))
- return;
-
- int height = rect.bottom - rect.top;
-
- GetClientRect( &rect );
- int width = rect.right;
-
- for( i = 1; i <= GetCountPerPage(); i++ )
- {
- dc.MoveTo( 0, top + height*i);
- dc.LineTo( width, top + height*i );
- }
- }
-
- // Do not call CListCtrl::OnPaint() for painting messages
-
- }
-
- /**
- * See if we should draw the grid.
- */
- BOOL CListCtrlEx::GetDrawGrid()
- {
- return( m_bDrawGrid );
- }
-
- /**
- * Set whether we should draw the grid or not.
- */
- void CListCtrlEx::SetDrawGrid(BOOL bDrawGrid )
- {
- m_bDrawGrid = bDrawGrid;
- // Force a redraw
- Invalidate();
- }
-
- LRESULT CListCtrlEx::OnSetBkColor(WPARAM wParam, LPARAM lParam)
- {
- m_clrBkgnd = (COLORREF)lParam;
- return(Default());
- }
-
- /*
- * This is another step to mimic the default behaviour of the list view
- * control. When the control loses focus, the focus rectangle around the
- * selected (focus) item has to be removed. When the control gets back
- * focus, then the focus rectangle has to be redrawn. Both these handlers
- * call the RepaintSelectedItems() helper function.
- */
-
- void CListCtrlEx::OnSetFocus(CWnd* pOldWnd)
- {
- CListCtrl::OnSetFocus(pOldWnd);
-
- // check if we are getting focus from label edit box
- // if(pOldWnd!=NULL && pOldWnd->GetParent()==this)
- // return;
-
- // repaint items that should change appearance
- if(m_bFullRowSel && (GetStyle() & LVS_TYPEMASK)==LVS_REPORT)
- RepaintSelectedItems();
- }
-
- LRESULT CListCtrlEx::OnSetTextBkColor(WPARAM wParam, LPARAM lParam)
- {
- m_clrTextBk = (COLORREF)lParam;
- return(Default());
- }
-
- LRESULT CListCtrlEx::OnSetTextColor(WPARAM wParam, LPARAM lParam)
- {
- m_clrText = (COLORREF)lParam;
- return(Default());
- }
-
- void CListCtrlEx::PreSubclassWindow()
- {
- CListCtrl::PreSubclassWindow();
-
- // Add initialization code for our header
- m_headerctrl.SubclassWindow( ::GetDlgItem(m_hWnd,0) );
-
- // Adding a tooltip to the header control.
- m_tooltip.Create( this );
- m_tooltip.AddTool( GetDlgItem(0), "Click to sort on this column. Drag to rearrange columns." );
-
- m_titletip.Create(this);
- m_titletip.SetBackground(CBrush(GetBkColor()));
- }
-
- void CListCtrlEx::SendSelChangedNotification()
- {
- NMHDR nmh;
- nmh.hwndFrom = *this;
- nmh.idFrom = GetDlgCtrlID();
- nmh.code = LVNU_SELCHANGED;
- GetParent()->SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmh);
- }
-
- void CListCtrlEx::ShowTitleTip(CPoint point)
- {
- int iRow;
- int iCol;
- CRect rcIcon;
-
- if(HitTestRowCol(point, iRow, iCol))
- {
- CRect cellrect = GetCellRect(iRow, iCol);
- // offset is equal to TextExtent of 2 space characters.
- // Make sure you have the right font selected into the
- // device context before calling GetTextExtent.
- // You can save this value as a member variable.
- // offset = pDC->GetTextExtent(_T(" "), 1 ).cx*2;
- int offset = 6;
- /*if(iCol == 0)
- {
- CRect rcLabel;
- GetItemRect(iRow, &rcLabel, LVIR_LABEL );
- offset = rcLabel.left - cellrect.left + offset / 2 - 1;
- }*/
-
- if(iCol == 0) // TBD: test this with IE4
- cellrect.left -= 2; // Does it also move the first column???
-
- cellrect.top++;
-
- if(GetItemState(iRow, LVIS_SELECTED)
- && (m_bFullRowSel || iCol == 0) )
- {
- m_titletip.SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));
- m_titletip.SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
- }
- else
- {
- m_titletip.SetBkColor(m_clrTextBk);
- m_titletip.SetTextColor(m_clrText);
- }
-
- // move over allowing for icon to show
- if ( iCol == 0 )
- {
- GetItemRect(iRow, rcIcon, LVIR_ICON);
- cellrect.left += rcIcon.Width();
- cellrect.right += rcIcon.Width();
- }
-
- // make sure our calculated result is where the mouse is.
- if( (point.x >= cellrect.left) && (point.x <= cellrect.right)
- && (point.y <= cellrect.bottom) && (point.y > cellrect.top) )
- m_titletip.Show(cellrect, GetTrueItemText(iRow, iCol), offset-1);
- }
- }
-
- void CListCtrlEx::OnHeaderClicked(NMHDR* pNMHDR, LRESULT* pResult)
- {
- HD_NOTIFY *phdn = (HD_NOTIFY *) pNMHDR;
-
- if( m_bUseGenericSort && phdn->iButton == 0 )
- { // User clicked on header using left mouse button
- if( phdn->iItem == nSortedCol )
- bSortAscending = !bSortAscending;
- else
- bSortAscending = TRUE;
-
- nSortedCol = phdn->iItem;
- SortTextItems( nSortedCol, bSortAscending );
- }
-
- *pResult = 0;
- }
-
-
- // SortTextItems - Sort the list based on column text
- // Returns - Returns true for success
- // nCol - column that contains the text to be sorted
- // bAscending - indicate sort order
- // low - row to start scanning from - default row is 0
- // high - row to end scan. -1 indicates last row
- BOOL CListCtrlEx::SortTextItems( int nCol, BOOL bAscending,
- int low, int high)
- {
- if( nCol >= ((CHeaderCtrl*)GetDlgItem(0))->GetItemCount() )
- return FALSE;
-
- ((CMyHeaderCtrl*)GetDlgItem( 0 ) )->RemoveAllSortImages();
- ((CMyHeaderCtrl*)GetDlgItem( 0 ) )->SetSortImage( nCol, bAscending );
- // SetItemSortState( iSubItem , (SORT_STATE)!ssEachItem );
-
- if( high == -1 ) high = GetItemCount() - 1;
-
- int lo = low;
- int hi = high;
- CString midItem;
-
- if( hi <= lo ) return FALSE;
-
- midItem = GetItemText( (lo+hi)/2, nCol );
-
- // loop through the list until indices cross
- while( lo <= hi )
- {
- // rowText will hold all column text for one row
- CStringArray rowText;
-
- // find the first element that is greater than or equal to
- // the partition element starting from the left Index.
- if( bAscending )
- while( ( lo < high ) && ( GetItemText(lo, nCol) < midItem ) )
- ++lo;
- else
- while( ( lo < high ) && ( GetItemText(lo, nCol) > midItem ) )
- ++lo;
-
- // find an element that is smaller than or equal to
- // the partition element starting from the right Index.
- if( bAscending )
- while( ( hi > low ) && ( GetItemText(hi, nCol) > midItem ) )
- --hi;
- else
- while( ( hi > low ) && ( GetItemText(hi, nCol) < midItem ) )
- --hi;
-
- // if the indexes have not crossed, swap
- // and if the items are not equal
- if( lo <= hi )
- {
- // swap only if the items are not equal
- if( GetItemText(lo, nCol) != GetItemText(hi, nCol))
- {
- // swap the rows
- LV_ITEM lvitemlo, lvitemhi;
- int nColCount =
- ((CHeaderCtrl*)GetDlgItem(0))->GetItemCount();
- rowText.SetSize( nColCount );
- int i;
- for( i=0; i<nColCount; i++)
- rowText[i] = GetItemText(lo, i);
- lvitemlo.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
- lvitemlo.iItem = lo;
- lvitemlo.iSubItem = 0;
- lvitemlo.stateMask = LVIS_CUT | LVIS_DROPHILITED |
- LVIS_FOCUSED | LVIS_SELECTED |
- LVIS_OVERLAYMASK | LVIS_STATEIMAGEMASK;
-
- lvitemhi = lvitemlo;
- lvitemhi.iItem = hi;
-
- GetItem( &lvitemlo );
- GetItem( &lvitemhi );
-
- for( i=0; i<nColCount; i++)
- SetItemText(lo, i, GetItemText(hi, i));
-
- lvitemhi.iItem = lo;
- SetItem( &lvitemhi );
-
- for( i=0; i<nColCount; i++)
- SetItemText(hi, i, rowText[i]);
-
- lvitemlo.iItem = hi;
- SetItem( &lvitemlo );
- }
-
- ++lo;
- --hi;
- }
- }
-
- // If the right index has not reached the left side of array
- // must now sort the left partition.
- if( low < hi )
- SortTextItems( nCol, bAscending , low, hi);
-
- // If the left index has not reached the right side of array
- // must now sort the right partition.
- if( lo < high )
- SortTextItems( nCol, bAscending , lo, high );
-
- return TRUE;
- }
-
-
- /**
- * User has finished editting a label.
- */
- void CListCtrlEx::OnEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult)
- {
- LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
- LV_ITEM *plvItem = &pDispInfo->item;
-
- if (plvItem->pszText != NULL)
- {
- SetItemText(plvItem->iItem, plvItem->iSubItem, plvItem->pszText);
- }
- *pResult = 0;
- }
-
- BOOL CListCtrlEx::SetItemText( int nItem, int nSubItem, LPCTSTR lpszText )
- {
- if ( nSubItem < 0 )
- return FALSE;
- else
- return( CListCtrl::SetItemText( nItem, nSubItem, lpszText ) );
- }
-
-
- void CListCtrlEx::EnableGenericSort(BOOL _use_generic)
- {
- m_bUseGenericSort = _use_generic;
- }
-